<?php
/**
 * Created by PhpStorm.
 * User: yuanxr
 * Date: 2018/6/10
 * Time: 上午8:17
 */

namespace Utility;
use Utility\Extend\EString;
use Utility\Encrypt\DES;
/**********************************************************************************************************************
 * /query：对queryString进行添加、删除、加密、解密、与数组互转等操作
 **********************************************************************************************************************/
class Query
{
    private $_EString;

    public function __construct()
    {
        $this->_EString = new EString();
    }
    /**
     * add
     *
     * @param string $str 源地址
     * @param string $key 需要添加的key
     * @param string $value 需要添加的value
     * @return string 添加一个参数到queryString，并将添加参数后的queryString返回
     */
    public function add(string $str, string $key, string $value)
    {

        // 带有host时的处理
        if ($this->_hashost($str)) {
            $str .= $this->_EString->has($str, '?') ? '&' : '?';
        }

        // 不带host时的处理
        else {
            $str .= $this->_EString->has($str, '=') ? '&' : '';
        }
        
        // 返回
        return $str . $key . '=' . $value;
    }

    /**
     * remove
     *
     * @param string $str 源地址
     * @param string $key 需要删除的key
     * @return string 从已有queryString中删除一个参数，并返回删除参数后的queryString
     */
    public function remove(string $str, string $key)
    {

        // 初始化result（在带有host时，首先将其赋值为host的值）
        $result = '';

        // 初始化query，查询字符串默认值为参数$str（不带host信息时，就使用此值）
        $query = $str;

        // 初始化是否带有host信息的flag
        $hasHost = $this->_hashost($str);

        // 如果带有host信息传入，则对query进行处理
        if ($hasHost) {

            // 检查是否带有 ? ，如果不带?说明无参数，直接返回
            if (!$this->_EString->has($str, '?')) {
                return $str;
            }

            // 否则，query等于去除host信息后的str，即 ? 之右的字符串
            $query = $this->_EString->right($str, '?');

            // 获取host的值，并赋值给result
            $result = $this->_EString->left($str, '?');
        }

        // 将query组合为数组
        $arrSrc = $this->enarray($query);
        $arrTarget = [];

        // 使用foreach去除键与key相同的参数（注意，此处不能使用arr::filter，因为在callback内部无法使用外部变量）
        foreach ($arrSrc as $k=>$v) {
            if ($k != $key) {
                $arrTarget[$k] = $v;
            }
        }

        // 查看最终剩余的参数数量，如果为空数组，则直接返回
        if (count($arrTarget) == 0) {
            return $result;
        }

        // 将arrTarget转换为sql字符串
        $query = $this->dearray($arrTarget);
        
        // 如果带有host，则连接result和query，并返回。否则直接返回query
        if ($hasHost) {
            return $result . '?' . $query;
        } else {
            return $query;
        }
    }

    /**
     * encrypt
     *
     * @param string $str 需要加密的源queryString
     * @return string 加密之后的queryString（通常用于各服务间的消息传递）
     */
    public function encrypt(string $str)
    {
        $des = new DES();
        return urlencode($des->encrypt($str));
    }

    /**
     * decrypt
     *
     * @param string $str 需要解密的源queryString
     * @return string 解密之后的queryString（通常用于各服务间的消息传递）
     */
    public function decrypt(string $str)
    {
        $des = new DES();
        return html_entity_decode($des->decrypt(urldecode($str)));
    }

    /**
     * enarray
     *
     * @param string $str 源queryString字符串
     * @return array 将queryString字符串转换成array对象，并返回
     */
    public function enarray(string $str)
    {
        parse_str($str, $arr);
        return $arr;
    }

    /**
     * dearray
     *
     * @param array $arr 需要转换为queryString的源数组
     * @return string 将array转换为queryString字符串，并返回
     */
    public function dearray(array $arr)
    {
        return http_build_query($arr);
    }

    /**
     * _hashost
     *
     * @return boolean 判断传入的源字符串是否包含host信息
     * add和remove方法即可以传入全路径，又可以传入参数字符串，需要使用这个方法进行判断
     * 包含host返回true，否则返回false
     */
    private function _hashost($str)
    {

        // 如果带有?，或者带有/，或者带有常用域名，说明存在host
        if ($this->_EString->has($str, '?') ||
                $this->_EString->has($str, '/') ||
                $this->_EString->has($str, '127.0.0.1') ||
                $this->_EString->has($str, '192.168') ||
                $this->_EString->has($str, 'localhost') ||
                $this->_EString->has($str, '.com') ||
                $this->_EString->has($str, '.net') ||
                $this->_EString->has($str, '.cn') ||
                $this->_EString->has($str, '.org')) {
            return true;
        }

        // 否则返回false
        return false;
    }
}
